#pragma once

#include <vex/core/implementation/handle_from_base.h>
#include <memory>

namespace vex {
    namespace core {

#ifdef VEX_HAS_CPP_11_FEATURE_SET_3
        template < class TBase, class ...TArgs >
        std::shared_ptr<TBase> make_shared_handle(TArgs&& ...p_args)
        {
            return std::make_shared< handle_from_base<TBase, unmanaged> >(
                std::forward<TArgs>(p_args)...);
        }

        template < class TBase, class TAllocator, class ...TArgs >
        std::shared_ptr<TBase> allocate_shared_handle(TAllocator&& p_alloc, TArgs&& ...p_args)
        {
            return std::allocate_shared< handle_from_base<TBase, unmanaged> >(
                std::forward<TAllocator>(p_alloc),
                std::forward<TArgs>(p_args)...);
        }

        template < class TBase, class TAllocator, class ...TArgs >
        std::shared_ptr<TBase> allocate_shared_handle_ext(TAllocator&& p_alloc, TArgs&& ...p_args)
        {
            return std::allocate_shared< handle_from_base<TBase, unmanaged> >(
                std::forward<TAllocator>(p_alloc),
                std::allocator_arg,
                std::forward<TAllocator>(p_alloc),
                std::forward<TArgs>(p_args)...);
        }
#else
        template < class TBase>
        std::shared_ptr<TBase> make_shared_handle()
        {
            return std::make_shared< handle_from_base<TBase, unmanaged> >();
        }

        template <class TBase, class TArg1>
        std::shared_ptr<TBase> make_shared_handle(
            TArg1&& p_arg1)
        {
            return std::make_shared< handle_from_base<TBase, unmanaged> >(
                std::forward<TArg1>(p_arg1));
        }

        template <class TBase, class TArg1, class TArg2>
        std::shared_ptr<TBase> make_shared_handle(
            TArg1 && p_arg1, TArg2 && p_arg2) 
        {
            return std::make_shared< handle_from_base<TBase, unmanaged> >(
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2));
        }

        template <class TBase, class TArg1, class TArg2, class TArg3>
        std::shared_ptr<TBase> make_shared_handle(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3) 
        {
            return std::make_shared< handle_from_base<TBase, unmanaged> >(
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2), 
                std::forward<TArg3>(p_arg3)); 
        }

        template <class TBase, class TArg1, class TArg2, class TArg3, class TArg4>
        std::shared_ptr<TBase> make_shared_handle(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4) 
        {
            return std::make_shared< handle_from_base<TBase, unmanaged> > (
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2), 
                std::forward<TArg3>(p_arg3), 
                std::forward<TArg4>(p_arg4)); 
        }
#endif
    }

    using core::make_shared_handle;
}